ノードの優先順位

[flow_graph.node_priorities]

フローグラフは、フローグラフ機能ノードの構築時に相対的な優先順位を設定するインターフェイスを提供し、グラフを実行するスレッドが高い優先順位のノードを優先するようにガイドします。


namespace oneapi { 
namespace tbb { 
namespace flow { 

    typedef unsigned int node_priority_t; 

    const node_priority_t no_priority = node_priority_t(0); 

} // namespace flow 
} // namespace tbb 
} // namespace oneapi

function_nodemultifunction_nodeasync_node および continue_node には、node_priority_t パラメーターを持つコンストラクターがあり、これはグラフノードの優先順位を設定します。パラメーターで指定される値が大きいほど優先順位は高くなります。パラメーターのデフォルト値である特殊な定数値 no_priority は、特定のノードの優先順位をオフにします。

特定のグラフでは、優先順位が指定されているノードを実行するタスクは、優先順位が設定されていないか低い優先順位のタスクよりも優先されます。スレッドが実行するタスクを検索する場合、グラフ内で実行可能な最も優先順位が高いタスクを選択します。

次の基本的な例は、グラフ内の 1 つのパスがほかのパスよりも優先される様子を示します。これは、グラフ全体のパフォーマンス向上に役立つことがあります。

./picture/critical_path_in_graph.png

依存関係フローグラフとクリティカル・パス

2 つのスレッドで上記のグラフを実行することを検討してください。ノード f1f3 の実行には同じ時間がかかり、f2 はそれ以上の時間がかかると想定します。そのためノード bs, f2 および fe がこのグラフのクリティカル・パスとなります。タスクを選択するる際の非決定論性により、oneTBB は最初にノード f1f3 を並行して実行する可能性があり、スレッドの 1 つがブロードキャスト・ノードの直後にノード f2 を選択する場合よりもグラフ全体の実行時間が長くなります。ノード f2 に高い優先順位を設定することでスレッドがクリティカル・パスをより速く実行するようになり、全体の実行時間を短縮できます。


#include <iostream> 
#include <cmath> 

#include "oneapi/tbb/tick_count.h" 
#include "oneapi/tbb/global_control.h" 

#include "oneapi/tbb/flow_graph.h」 

void spin_for( double delta_seconds ) { 
    oneapi::tbb::tick_count start = oneapi::tbb::tick_count::now(); 
    while( (oneapi::tbb::tick_count::now() - start).seconds() < delta_seconds ) ; 
} 

static const double unit_of_time = 0.1; 

struct Body { 
    unsigned factor; 
    Body( unsigned times ) : factor( times ) {} 
    void operator()( const oneapi::tbb::flow::continue_msg& ) { 
    // body execution takes 'factor' units of time spin_for( factor * unit_of_time ); 
    } 
}; 

int main() { 
    using namespace oneapi::tbb::flow; 

    const int max_threads = 2; 
    oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, max_threads); 

    graph g; 

    broadcast_node<continue_msg> bs(g); 

    continue_node<continue_msg> f1(g, Body(5)); 

    // f2 is a heavy one and takes the most execution time as compared to the other nodes in the 
    // graph.     Therefore, let the graph start this node as soon as possible by prioritizing it over 
    // the other nodes. 
    continue_node<continue_msg> f2(g, Body(10), node_priority_t(1)); 

    continue_node<continue_msg> f3(g, Body(5)); 

    continue_node<continue_msg> fe(g, Body(7)); 

    make_edge( bs, f1 ); 
    make_edge( bs, f2 ); 
    make_edge( bs, f3 ); 

    make_edge( f1, fe ); 
    make_edge( f2, fe ); 
    make_edge( f3, fe ); 

    oneapi::tbb::tick_count start = oneapi::tbb::tick_count::now(); 

    bs.try_put( continue_msg() ); 
    g.wait_for_all(); 

    double elapsed = std::floor((oneapi::tbb::tick_count::now() - start).seconds() / unit_of_time); 

    std::cout << "Elapsed approximately " << elapsed << " units of time" << std::endl; 

    return 0; 
}